﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using NativeWifi;

namespace Wardriver
{
    /// <summary>
    /// Class to do actual Wardriving.
    /// This class combines both Wifi Scanning and GPS coordinate gathering.
    /// </summary>
    public class Wardriver
    {
        /// <summary>
        /// Wifi Client Container.
        /// </summary>
        private WlanClient _wificlient;

        /// <summary>
        /// Internal wifi data. Holds access points and GPS data.
        /// </summary>
        private List<WifiAP> _wifiAP;
        private object _wifiAP_Lock;

        private System.Timers.Timer _intervaltimer;

        /// <summary>
        /// Initiates the Wardriver.
        /// </summary>
        public Wardriver() 
        {
            _wificlient = new WlanClient();
            _wifiAP = new List<WifiAP>();

            _wifiAP_Lock = new object();

            _intervaltimer = new System.Timers.Timer();
            _intervaltimer.Interval = 3000;
            _intervaltimer.Elapsed += new System.Timers.ElapsedEventHandler(_intervaltimer_Elapsed);
        }

        /// <summary>
        /// Returns the WifiAP list. 
        /// </summary>
        public List<WifiAP> GetWifiAP()
        {
            return GetWifiAP(false);
        }

        /// <summary>
        /// Returns the WifiAP list.
        /// </summary>
        /// <param name="Clear">If True, clears the AP last afterwards.</param>
        /// <returns></returns>
        public List<WifiAP> GetWifiAP(bool Clear)
        {
            List<WifiAP> temp = new List<WifiAP>();

            lock (_wifiAP_Lock)
            {
                foreach (WifiAP item in _wifiAP)
                {
                    temp.Add(item);
                }

                if (Clear)
                    _wifiAP = new List<WifiAP>();
            }

            // Return
            return temp;
        }

        /// <summary>
        /// Clears the WifiAP list. Useful if you've just exported the entire thing to another datasource.
        /// </summary>
        public void ClearAPs() 
        {
            // Clear
            lock (_wifiAP_Lock)
            {
                _wifiAP = new List<WifiAP>();
            }
        }

        /// <summary>
        /// Starts the internal timer, to obtain a constant stream of data.
        /// </summary>
        public void StartTimer() 
        {
            StartTimer(_intervaltimer.Interval);
        }

        /// <summary>
        /// Starts the internal timer, to obtain a constant stream of data.
        /// </summary>
        /// <param name="Interval">The interval at which to ping data and stuff. Minimum 1000 ms.</param>
        public void StartTimer(double Interval) 
        {
            if (Interval < 1000) Interval = 1000;

            // Stop if already running.
            if (_intervaltimer.Enabled) _intervaltimer.Stop();

            _intervaltimer.Interval = Interval;
            _intervaltimer.Start();
        }

        /// <summary>
        /// Stops the internal timer.
        /// </summary>
        public void StopTimer() 
        {
            _intervaltimer.Stop();
        }

        void _intervaltimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            // Do a scan
            WifiEntry[] twifi = GetWifi();

            // Get GPS data
            GPSEntry tgps = GetGPS();

            if (twifi.Length <= 0) { return; }

            // Append GPS Data to Wifi Entries
            for (int x = 0; x < twifi.Length; x++)
            {
                twifi[x].GPSData = tgps;
            }

            // Append to information
            for (int x = 0; x < twifi.Length; x++)
            {
                AppendAPPointData(twifi[x]);
            }
        }

        private void AppendAPPointData(WifiEntry data) 
        {
            int _index = -1;

            lock (_wifiAP_Lock)
            {
                // Find if the AP exists
                for (int x = 0; x < _wifiAP.Count; x++)
                {
                    if (_wifiAP[x].MAC_String == data.MAC_String && _wifiAP[x].SSID == data.SSID) 
                    {
                        _index = x;
                        break;
                    }
                }

                if (_index != -1)
                {
                    // Find smallest distance to any other given GPS point
                    double _SmallestDistance = double.MaxValue;
                    List<WifiEntry> points = GetWifiAP()[_index].GetDataPoints();
                    for (int x = 0; x < points.Count; x++)
                    {
                        double dist = data.GPSData.Distance(points[x].GPSData);
                        if (dist < _SmallestDistance) _SmallestDistance = dist;
                    }

                    // If minimum distance is less than 1M (Metre). Ignore.
                    if (_SmallestDistance >= 0.001)
                    {
                        // Update
                        _wifiAP[_index].AppendDataPoint(data);
                    }
                }
                else
                {
                    // Add AP
                    _wifiAP.Add(new WifiAP(data));
                }
            }
        }

        /// <summary>
        /// This function obtains a snapshot of Wifi networks.
        /// </summary>
        public WifiEntry[] GetWifi()
        {
            // Result
            List<WifiEntry> res = new List<WifiEntry>();

            // Iterate Interfaces
            foreach (WlanClient.WlanInterface item in _wificlient.Interfaces)
            {
                // Iterate Access Points.
                Wlan.WlanBssEntry[] list = item.GetNetworkBssList();

                foreach (Wlan.WlanBssEntry AP in list)
                {
                    res.Add(new WifiEntry(AP));
                }
            }

            // Return
            return res.ToArray();
        }
        
        /// <summary>
        /// This function obtains a snapshot of the GPS location.
        /// </summary>
        public GPSEntry GetGPS() 
        {
            GPSEntry p = new GPSEntry(1, 2);
            return p;
        }
    }
}
